package com.dtflys.easyel.parser;

import com.dtflys.easyel.ast.ASTConstant;
import com.dtflys.easyel.ast.ASTNode;
import com.dtflys.easyel.ast.ASTType;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.dtflys.easyel.parser.EToken.DECIMAL;
import static com.dtflys.easyel.parser.EToken.DIV;
import static com.dtflys.easyel.parser.EToken.DOUBLE;
import static com.dtflys.easyel.parser.EToken.FLOAT;
import static com.dtflys.easyel.parser.EToken.HEX;
import static com.dtflys.easyel.parser.EToken.IDENTIFIER;
import static com.dtflys.easyel.parser.EToken.INTEGER;
import static com.dtflys.easyel.parser.EToken.MUL;
import static com.dtflys.easyel.parser.EToken.OCT;

public class EParser {
    
    private final ETokenStream tokenStream;
    
    public static class ParserContext {
        final ETokenStream tokenStream;
        final EToken emptyToken;
        List<EToken> tokens;
        ASTNode astNode;

        public ParserContext(final ETokenStream tokenStream) {
            this.tokenStream = tokenStream;
            emptyToken = new EToken(tokenStream.getLexer().getSource(), -1, "[EMPTY]", -1, -1, -1);
            tokens = new ArrayList<>(8);
        }

        public EToken current() {
            if (tokens.isEmpty()) {
                try {
                    tokens.add(tokenStream.nextToken());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (tokens.isEmpty()) {
                return emptyToken;
            }
            return tokens.get(0);
        }
        
        
        public EToken get(int index) {
            
            if (tokens.isEmpty() || tokens.size() <= index) {
                return emptyToken;
            }
            return tokens.get(index);
        }
        
        public ParserContext node(final ASTNode astNode) {
            this.astNode = astNode;
            return this;
        }
    }
    

    public EParser(ETokenStream tokenStream) {
        this.tokenStream = tokenStream;
    }
    
    private boolean match(final ParserContext context, final int type) {
        return type == context.current().getType();
    }
    
    
    public ASTNode parse() throws IOException {
        return null;
    }
    
    
    private boolean express(final ParserContext context) {
        
        if (express(context) && match(context, MUL) && express(context)) {
            return true;
        }
        if (express(context) && match(context, DIV) && express(context)) {
            return true;
        }
        if (literal(context)) {
            return true;
        }

        return false;
    }
    
    private boolean literal(final ParserContext context) {
        return identifier(context) || number(context);
    }
    
    private boolean identifier(final ParserContext context) {
        if (match(context, IDENTIFIER)) {
            return true;
        }
        return false;
    }
    
    private boolean number(final ParserContext context) {
        return integer(context) || decimal(context);
    }
    
    private boolean integer(final ParserContext context) {
        switch (context.current().type) {
            case INTEGER:
            case HEX:
            case OCT:
                context.node(new ASTConstant(context.current(), ASTType.INTEGER, context.current()));
                return true;
            default:
                return false;
        }
    }
    
    private boolean decimal(final ParserContext context) {
        switch (context.current().type) {
            case FLOAT:
            case DOUBLE:
            case DECIMAL:
                context.node(new ASTConstant(context.current(), ASTType.INTEGER, context.current()));
                return true;

            default:
                return false;
        }
    }
    
}
